home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / TURB_VIS / TVLIST / TVLIST.PAS < prev   
Pascal/Delphi Source File  |  1991-03-17  |  38KB  |  1,091 lines

  1. unit TVLIST;
  2.   interface
  3.   uses Objects, App, Drivers, Views, Dialogs, msgBox;
  4.   {==================================================================
  5.    TVLIST
  6.  
  7.    This unit implements a set of TCollection types and TDialog types
  8.    that facilitates the use of Lists and Listboxes.  Two abstract
  9.    classes are defined, TLIST and TSORTEDLIST that provide for expanded
  10.    TCollection functions. These Classes allow you to create instances
  11.    of TCollections with members of ANY data type and still use them
  12.    with a Listbox.  These are ABSTRACT classes, and virtual methods
  13.    must be defined for each of your list types.  Both sequential and
  14.    sorted lists are supported. Two classes TBOXER and TSORTEDBOXER
  15.    are defined that provide TListBox functionality.  Finally, two
  16.    classes TLISTDIALOG and TSORTEDLISTDIALOG provides an advanced
  17.    Dialog for use of listboxes.  This class can enable adding to the
  18.    lists, delete list items, editing list items, and search and
  19.    selection from the list.  All or none of these capabilities can
  20.    be selected. Also provided is a class LISTBOXINPUTLINE which can
  21.    be inserted into other Dialog boxes and when selected in these
  22.    dialog boxes will execute a TLISTDIALOG .
  23.  
  24.    Use the Compiler Defination of Define RegisterStreams to cause the
  25.    Unit to register Plist and PSortedList in its initialization code,
  26.    other wise registration must be done using RegisterTVList.
  27.  
  28.    Refer to TVLIST.DOC for documentation.  Refer to DEMO.PAS for examples
  29.    of use.
  30.  
  31.    Copyright 1991 McQuay Technologies
  32.      2329 E. Cortez Phoenix AZ 85028
  33.      100 Sycamore Richmond TX
  34.      Prodigy ID WPTD01E Compuserve 72307,320
  35.      Released into the Public Domain, Give Credit were Credit Is Due
  36.    ==================================================================}
  37.  
  38.   {==================================================================
  39.    TList  abstract Class
  40.    ==================================================================}
  41.    const
  42.      EndOfCollection = -1;    { Defines that Item was not found or
  43.                                 and Item Was not selected by TLISTDIALOG }
  44.  
  45.   type
  46.     PList = ^TList;
  47.     TList = object(TCollection)
  48.       function CreateItem(Corner:Tpoint):pointer; virtual;
  49.       procedure editItem(Corner:Tpoint;Item:pointer); virtual;
  50.       function GetItemText(item:pointer;MaxLen:word):string; virtual;
  51.       function AtAddNewItem(Corner:Tpoint;Index:integer):pointer;
  52.       function MaxTextLength:word;
  53.       end;
  54.   {==================================================================
  55.    TSortedList  abstract Class
  56.    ==================================================================}
  57.   type
  58.     PSortedList = ^TSortedList;
  59.     TSortedList = object(TSortedCollection)
  60.       function CreateItem(Corner:Tpoint):pointer; virtual;
  61.                { Override : Required
  62.                  This function creates a Collection Object and
  63.                  returns a pointer to it.  This could use a
  64.                  dialog box or any other method to obtain
  65.                  data needed to create an instance of your
  66.                  collection object.  Must return a nil
  67.                  pointer id no object was created. This method
  68.                  can be left as is (no override) if Adding to a
  69.                  list with TListDialog is not desired.}
  70.       procedure editItem(Corner:TPoint;Item:pointer); virtual;
  71.                { Override : Required
  72.                  This function edits the Item.  Likely will
  73.                  use a dialog box, but could use something
  74.                  else. This method can be left as is (no
  75.                  override) if Editing a list element with
  76.                  TListDialog is not desired.}
  77.       function GetItemText(item:pointer;MaxLen:word):string; virtual;
  78.                { Override : Required
  79.                  This function returns a string that represents
  80.                  the data in your Collection Object item.  This will
  81.                  be used by the listbox to display data in your
  82.                  object item }
  83.       function AtAddNewItem(Corner:TPoint):pointer;
  84.                { Override : Never
  85.                  adds an item to the list at the Index position using
  86.                  Atinsert(index,CreateItem).  Use Count for index to
  87.                  add to end, 0 to add to top.  Will return a pointer
  88.                  to new item.  Should return a nil if not succesful.}
  89.       function MaxTextLength:word;
  90.                { Override : Never
  91.                  Uses Foreach and GetItemText(,256) to determine length
  92.                  of longest string. }
  93.       end;
  94.   {==================================================================
  95.    TListBoxer  Class
  96.    ==================================================================}
  97.    type
  98.    PListBoxer = ^TListBoxer;
  99.    TListBoxer = object(TListBox)
  100.      function GetText(Item:Integer; MaxLen:integer):string; virtual;
  101.      procedure HandleEvent(var Event:TEvent); virtual;
  102.      end;
  103.   {==================================================================
  104.    TSortedListBoxer  Class
  105.    ==================================================================}
  106.    type
  107.    PSOrtedListBoxer = ^TSortedListBoxer;
  108.    TSortedListBoxer = object(TListBoxer)
  109.      function GetText(Item:Integer; MaxLen:integer):string; virtual;
  110.      end;
  111.   {==================================================================
  112.    TList an TListDialog Support Constants and Types
  113.    ==================================================================}
  114.    const
  115.  
  116.    { Behavior Constants }
  117.      sfAdd    = $1;
  118.      sfDelete = $2;
  119.      sfEdit   = $4;
  120.      sfSearch = $8;
  121.      sfPromptDelete = $10;
  122.      SfPromptExit = $20;
  123.      sfFullEdit = sfAdd + sfDelete + sfEdit;
  124.      sfDoall  = $FF;
  125.  
  126.   type
  127.     TListRec = record
  128.       Item:pointer;
  129.       Index:integer;
  130.       end;
  131.  
  132.   {==================================================================
  133.    TListDialog  Class
  134.    ==================================================================}
  135.   type
  136.    PListDialog = ^TListDialog;
  137.    TListDialog = object(TDialog)
  138.      AB : byte;
  139.      TLR:TListRec;
  140.      Max:byte;
  141.      List:pointer;
  142.      LB:PlistBox;
  143.      X,Y:word;
  144.      SearchString:PString;
  145.      constructor init(var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  146.                       TheList : PList; BoxHeader:TTitleStr);
  147.      procedure   BASICinit(var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  148.                            MaxStringLen:byte); virtual;
  149.      function  DataSize: word; virtual;
  150.      procedure GetData(var rec); virtual;
  151.      procedure SetData(var rec); virtual;
  152.      procedure HandleEvent(var Event:TEvent); virtual;
  153.      end;
  154.  
  155.   {==================================================================
  156.    TSortedListDialog  Class
  157.    ==================================================================}
  158.    PSortedListDialog = ^TSortedListDialog;
  159.    TSortedListDialog = object(TListDialog)
  160.      constructor init(var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  161.                       TheList : PSortedList; BoxHeader:TTitleStr);
  162.      procedure HandleEvent(var Event:TEvent); virtual;
  163.      end;
  164.   {==================================================================
  165.    TListDialogInputField  Class
  166.    ==================================================================}
  167.   type
  168.   PListDialogInputField= ^TListDialogInputField;
  169.   TListDialogInputField= object(TInputLine)
  170.     TD:pointer;  { Pointer to Dialog }
  171.     TL:pointer;  { Pointer to List   }
  172.     max:byte;
  173.     Index:word;
  174.     Sorted:boolean;
  175.     constructor init (Field:TPoint;ListLocation:Tpoint;ListHeight:word;
  176.                       Title:String;Behavior:byte;AList:Pointer;
  177.                       BoxHeader:string;SortedList:boolean);
  178.     function  DataSize:word; virtual;
  179.     procedure GetData(Var Rec); virtual;
  180.     procedure SetData(Var Rec); virtual;
  181.     procedure HandleEvent(var Event:TEvent); virtual;
  182.     end;
  183.   {==================================================================
  184.    TVList Resource Registration
  185.    ==================================================================}
  186.    procedure RegisterTVList;
  187.   {==================================================================
  188.    Utilities
  189.    ==================================================================}
  190.    procedure MakeTrect(Corner:Tpoint;Xsize,YSize:integer;var Bounds:Trect);
  191.    Procedure TPointAssign(var P:TPoint; X,Y:integer);
  192. {=============================================================}
  193.  implementation
  194.  const
  195.    { Stream Registration Constants }
  196.    RList : TStreamRec = (
  197.      ObjType:200;
  198.      VmtLink: ofs(TypeOf(Tlist)^);
  199.      Load:@Tlist.load;
  200.      Store:@TList.Store);
  201.    RSortedList :TStreamRec = (
  202.      ObjType:201;
  203.      VmtLink:ofs(TypeOf(TSortedList)^);
  204.      Load:@TSortedList.load;
  205.      Store:@TSortedList.Store);
  206.  
  207.  
  208.    { TlistDialog INternal Commands }
  209.    const
  210.        tldAdd    = $2001;
  211.        tldEdit   = $2002;
  212.        tldDelete = $2003;
  213.        tldPicked = $2004;
  214.  
  215.    { Map for writestr under TDialog }
  216.        SearchPaletteMap = 28;
  217.   {==================================================================
  218.    Utilities
  219.    ==================================================================}
  220.   function Lput(source:string;width:word):string;
  221.     var
  222.       Temp:string[80];
  223.     begin
  224.       if length(source)>width then
  225.         Lput := copy(source,1,width)
  226.       else
  227.         begin
  228.         fillchar(Temp[1],width-length(source),32);
  229.         Temp[0] := char(width-length(source));
  230.         Lput := source + Temp;
  231.         end;
  232.       end;
  233.    {-----------------------------------}
  234.    Procedure TPointAssign(var P:TPoint; X,Y:integer);
  235.      begin
  236.      P.X := X;
  237.      P.Y := Y;
  238.      end;
  239.    {-----------------------------------}
  240.    procedure MakeTrect(Corner:Tpoint;Xsize,YSize:integer;var Bounds:Trect);
  241.      var
  242.        DX,DY:integer;
  243.        SH:byte;
  244.      begin
  245.      SH := ScreenHeight-2;
  246.      with Corner do
  247.        begin
  248.        DX := (X+XSize)-1;
  249.        DY := (Y+YSize)-1;
  250.        if DX>ScreenWidth then
  251.          if (XSize>ScreenWidth) then
  252.            begin
  253.            X := 0;
  254.            DX := ScreenWidth;
  255.            end
  256.          else
  257.            begin
  258.            X := X-(DX-ScreenWidth);
  259.            DX := (X+Xsize)-1;
  260.            end;
  261.        if DY>SH then
  262.          if (YSize>SH) then
  263.            begin
  264.            Y := 0;
  265.            DY := SH;
  266.            end
  267.          else
  268.            begin
  269.            Y := Y-(DY-SH);
  270.            DY := (Y+Ysize)-1;
  271.            end;
  272.       end;
  273.      Bounds.assign(Corner.X,Corner.Y,DX,DY);
  274.   end;
  275.   {==================================================================
  276.    TListBoxer  Class
  277.    ==================================================================}
  278.    procedure TListBoxer.HandleEvent(var Event:TEvent);
  279.      var
  280.        i:word;
  281.      begin
  282.      with Event do
  283.        if ((What=evKeyDown) and (keycode=kbEnter)) or
  284.           ((What=evBroadCast) and (Command=cmListItemSelected)) then
  285.             begin
  286.             What := evCommand;
  287.             Command := tldPicked;
  288.             end
  289.        else
  290.            TListbox.HandleEvent(Event);
  291.      end;
  292.    {-----------------------------------}
  293.    function TListBoxer.GetText(Item:Integer; MaxLen:integer):string;
  294.        var
  295.          P:pointer;
  296.          T:string;
  297.      begin
  298.      P:= List^.At(Item);
  299.      T:= Plist(List)^.GetItemText(P,MaxLen);
  300.      GetText := T;
  301.      end;
  302.  
  303.   {==================================================================
  304.    TSortedListBoxer  Class
  305.    ==================================================================}
  306.    {-----------------------------------}
  307.    function TSortedListBoxer.GetText(Item:Integer; MaxLen:integer):string;
  308.        var
  309.          P:pointer;
  310.          T:string;
  311.      begin
  312.      P:= List^.At(Item);
  313.      T:= PSOrtedlist(List)^.GetItemText(P,MaxLen);
  314.      GetText := T;
  315.      end;
  316.    {-----------------------------------}
  317.  
  318.   {==================================================================
  319.    TList  abstract Class
  320.    ==================================================================}
  321.     function TList.CreateItem(Corner:TPoint):pointer;
  322.       begin CreateItem := nil end;
  323.     {------------------------------------}
  324.       procedure TList.editItem(Corner:TPoint;Item:pointer);
  325.       begin end;
  326.     {------------------------------------}
  327.     function TList.GetItemText(item:pointer;MaxLen:word):string;
  328.       begin
  329.       Abstract;
  330.       end;
  331.     {------------------------------------}
  332.     function TList.AtAddNewItem(Corner:TPoint;Index:integer):pointer;
  333.       var P:pointer;
  334.       begin
  335.       P := CreateItem(Corner);
  336.       if P<>nil then
  337.         AtInsert(Index,P);
  338.       AtAddNewItem := P;
  339.       end;
  340.     {------------------------------------}
  341.     function TList.MaxTextLength:word;
  342.       var
  343.         Tmax:word;
  344.       procedure GetMAx(P:pointer); far;
  345.         { Simply searches list and finds longest string }
  346.         var
  347.           I:word;
  348.           Temp:string;
  349.         begin
  350.         if P<>nil then
  351.           begin
  352.           Temp := GetItemText(P,$ff);
  353.           i:=length(Temp);
  354.           if i>TMax then TMax := i;
  355.           end;
  356.         end;
  357.  
  358.       begin
  359.         TMax := 0;
  360.         foreach(@GetMax);
  361.         MaxTextLength := Tmax;
  362.       end;
  363.   {==================================================================
  364.    TSortedList  abstract Class
  365.    ==================================================================}
  366.     function TSortedList.CreateItem(Corner:TPoint):pointer;
  367.       begin  CreateItem := nil end;
  368.     {------------------------------------}
  369.       procedure TSortedList.editItem(Corner:TPoint;Item:pointer);
  370.       begin end;
  371.     {------------------------------------}
  372.     function TSortedList.GetItemText(item:pointer;MaxLen:word):string;
  373.       begin
  374.       Abstract;
  375.       end;
  376.     {------------------------------------}
  377.     function TSortedList.AtAddNewItem(Corner:TPoint):pointer;
  378.       var P:pointer;
  379.       begin
  380.       P := CreateItem(Corner);
  381.       if P<>nil then
  382.         Insert(P);
  383.       AtAddNewItem := P;
  384.       end;
  385.     {------------------------------------}
  386.     function TSortedList.MaxTextLength:word;
  387.       var
  388.         Tmax:word;
  389.       procedure GetMAx(P:pointer); far;
  390.         { Simply searches list and finds longest string }
  391.         var
  392.           I:word;
  393.           Temp:string;
  394.         begin
  395.         if P<>nil then
  396.           begin
  397.           Temp := GetItemText(P,$ff);
  398.           i:=length(Temp);
  399.           if i>TMax then TMax := i;
  400.           end;
  401.         end;
  402.  
  403.       begin
  404.         TMax := 0;
  405.         foreach(@GetMax);
  406.         MaxTextLength := Tmax;
  407.       end;
  408.  
  409.   {==================================================================
  410.    TListDialog   Class
  411.    ==================================================================}
  412.      const
  413.        NoSortIndent = 5;
  414.        SortIndent = 18;
  415.        TopIndent = 11;
  416.      procedure   TListDialog.BASICinit(var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  417.                            MaxStringLen:byte);
  418.        var
  419.          PV:PView;
  420.          i:word;
  421.          R:Trect;
  422.        begin
  423.        { Minimum width for OK and Cancel is 10}
  424.         if MaxStringLen<10 then MaxStringLen:= 11;
  425.  
  426.        { Now if Buttons needed make sure Dialog is wide enough for
  427.          text and Buttons (add column width here later )}
  428.         if (sfFullEdit and Behavior)>0 then
  429.           i:=SortIndent else i:= NoSortIndent;
  430.         with Bounds do
  431.           if ((B.X - A.X)) < MaxStringLen+i then
  432.               B.X:=A.X+MaxStringLen+i;
  433.  
  434.        { Now Check if adequate height provided for list and
  435.          OK and Cancel Buttons, List can be minimum 4 items high. }
  436.         i := TopIndent;
  437.         if (sfSearch and Behavior)=0 then
  438.           dec(i);
  439.         with Bounds do
  440.           if (B.Y-A.Y)<i then B.Y := A.Y+i;
  441.  
  442.        { Ok init Dialog }
  443.         TDialog.init(Bounds,ATitle);
  444.  
  445.        { Save Max }
  446.          Max := MaxStringLen;
  447.  
  448.        { Set Behavior }
  449.         AB := Behavior;
  450.        { Can not have search here }
  451.         AB := AB and $F7;
  452.  
  453.        { Set Clear Record }
  454.         with TLR do
  455.          begin
  456.          Item:=nil;
  457.          Index:=-1;
  458.          end;
  459.  
  460.        { Ok Setup Search String Area if selected }
  461.         if (sfSearch and Behavior)>0 then
  462.           begin
  463.           X := 1;
  464.           Y := 1;
  465.           end
  466.         else
  467.          begin
  468.          X := 0;
  469.          Y := 0;
  470.          end;
  471.        { Setup Buttons }
  472.         if (sfFullEdit and AB)>0 then
  473.           begin
  474.           R.assign(Max+5,2,Max+13,4);
  475.           if (sfAdd and AB)>0 then
  476.             insert(new(PButton, init(R,' Add ',tldAdd,bfnormal)));
  477.           if (sfedit and AB)>0 then
  478.             begin
  479.             R.assign(Max+5,4,Max+14,6);
  480.             insert(new(PButton, init(R,' Edit ',tldedit,bfnormal)));
  481.             end;
  482.           if (sfdelete and AB)>0 then
  483.             begin
  484.             R.assign(Max+5,6,Max+16,8);
  485.             insert(new(PButton, init(R,' Delete ',tlddelete,bfnormal)));
  486.             end;
  487.           end;
  488.         { add OK and Cancel }
  489.           I := (Bounds.B.Y-Bounds.A.Y) - 3;
  490.           R.assign(1,i,6,I+2);
  491.           insert(new(PButton, init(R,'Ok',cmOk,bfnormal)));
  492.           R.assign(6,i,15,i+2);
  493.           insert(new(PButton, init(R,'Cancel',cmCancel,bfDefault)));
  494.  
  495.      end;
  496.     {------------------------------------------------------------------}
  497.  
  498.      constructor TListDialog.init
  499.                    (var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  500.                     TheList : PList; BoxHeader:TTitleStr);
  501.       var
  502.         R:Trect;
  503.         SB:PSCrollBar;
  504.         i:word;
  505.         TMax:word;
  506.       {-------------------------------------}
  507.         begin
  508.        { Get Max Text Width of Tlist Items }
  509.         Tmax := TheList^.MaxTextLength;
  510.  
  511.         BASICinit(Bounds,ATitle,Behavior,TMax);
  512.  
  513.        { Save List }
  514.         List := TheList;
  515.  
  516.        { Ok now set up a scrollbar }
  517.         i:=(Bounds.B.Y-Bounds.A.Y)-4;
  518.         R.assign(Max+2,Y+2,Max+3,i);
  519.         SB := new(PScrollBar, init(R));
  520.         insert(SB);
  521.  
  522.        { Ok now setup ListBox }
  523.         R.assign(1,Y+2,Max+2,i);
  524.         LB := new(PlistBoxer, init(R,1,SB));
  525.        { Setup Initial Data to List Box, will be chnaged by
  526.          SetData later}
  527.         LB^.newlist(TheList);
  528.         LB^.FocusItem(0);
  529.         insert(LB);
  530.         { Add Box Header }
  531.         if BoxHeader <> '' then
  532.           begin
  533.           R.assign(1,Y+1,length(BoxHeader)+2,Y+2);
  534.           insert(new(Plabel,init(R,BoxHeader,LB)));
  535.           end;
  536.         end;
  537.     {-------------------------------------------------}
  538.      function  TListDialog.DataSize: word;
  539.        begin
  540.        DataSize := sizeof(TLR);
  541.        end;
  542.     {-------------------------------------------------}
  543.      procedure TListDialog.GetData(var rec);
  544.        begin
  545.        move(TLR,rec,DataSize);
  546.        end;
  547.     {-------------------------------------------------}
  548.      procedure TListDialog.SetData(var rec);
  549.        begin
  550.        move(rec,TLR,dataSize);
  551.        if (TLR.index>0)and(TLR.INDEX<PLIST(List)^.count) then
  552.          LB^.focusItem(TLR.index);
  553.        end;
  554.     {-------------------------------------------------}
  555.      procedure TListDialog.HandleEvent(var Event:TEvent);
  556.      var
  557.        Affirmative : word;
  558.        FocusedIndex:integer;
  559.        FocusedItem:pointer;
  560.        NextEvent:TEvent;
  561.        MsgStr,ParamStr:Pstring;
  562.        R:TRect;
  563.       {--------------------------}
  564.       procedure UpdateLB(Index:integer);
  565.         begin
  566.       { A real No No ! But it is the only way to update
  567.         LB and keep the List from being disposed! }
  568.         LB^.List := nil;
  569.         LB^.newlist(List);
  570.         LB^.focusItem(Index);
  571.         LB^.drawview;
  572.         end;
  573.       {--------------------------}
  574.  
  575.      begin
  576.       if (Event.What=evCommand) then
  577.         case Event.Command of
  578.          { OK It was selected we are ready to exit, Save data }
  579.          cmOk:
  580.            with TLR do
  581.              begin
  582.              Index := LB^.Focused;
  583.              Item:=  PList(List)^.at(Index);
  584.              end;
  585.          { Whoops, a cancel, make sure nil is loaded }
  586.          cmCancel,CmQuit:
  587.            with TLR do
  588.              begin
  589.              Index := EndOfCollection;
  590.              Item:=  nil;
  591.              end;
  592.          end;
  593.        TDialog.HandleEvent(Event);
  594.        if LB^.GetState(sfFocused) then
  595.              LB^.HandleEvent(Event);
  596.        FocusedIndex := LB^.Focused;
  597.        with Event do
  598.            case What of
  599.              evCommand:
  600.                case Command of
  601.               { Ok it was picked }
  602.                  tldpicked:
  603.                    begin
  604.                    with NextEvent do
  605.                      { If prompt then move to OK Button }
  606.                      if (AB and sfPromptExit)>0 then
  607.                        begin
  608.                        Selectnext(true);
  609.                        Selectnext(true);
  610.                        end
  611.                      else
  612.                        { Else Set CmOK }
  613.                        begin
  614.                        What := evCommand;
  615.                        command := cmOk;
  616.                        end;
  617.                    putevent(NextEvent);
  618.                    end;
  619.  
  620.               { Add Record }
  621.                  tldAdd:
  622.                    with PList(List)^ do
  623.                      begin
  624.                    { OK Add a new Item, check if nil afterward }
  625.                      R.Assign(1,1,0,0);
  626.                      MakeGlobal(R.A,R.A);
  627.                      FocusedItem := AtAddNewItem(R.A,FocusedIndex);
  628.                      if FocusedItem <> nil then
  629.                        begin
  630.                        FocusedIndeX := indexOf(FocusedItem);
  631.                        UpdateLB(FocusedIndex);
  632.                        end;
  633.                      end;
  634.  
  635.                { Edit Record }
  636.                  tldEdit:
  637.                    begin
  638.                    R.Assign(1,1,0,0);
  639.                    MakeGlobal(R.A,R.A);
  640.                    with PList(List)^ do
  641.                      EditItem(R.A,PList(List)^.at(LB^.Focused));
  642.                    LB^.drawview;
  643.                    end;
  644.  
  645.                { Delete Record }
  646.                  tldDelete:
  647.                    { Make sure something is there}
  648.                    if PList(list)^.count>0 then
  649.                      begin
  650.  
  651.                      { If prompt then prompt }
  652.                      if (AB and sfPromptDelete)>0 then
  653.                        begin
  654.                        with PList(List)^ do
  655.                          ParamStr := newstr(GetItemText(AT(FocusedIndex),Max));
  656.                          MsgStr := newStr('Delete: %s');
  657.                        Affirmative :=
  658.                          MessageBox(MsgSTr^,@ParamStr,
  659.                                     MFConfirmation+MfYesButton+MfNoButton);
  660.                        disposestr(Paramstr);
  661.                        disposestr(MsgStr);
  662.                        end
  663.                      else
  664.                        Affirmative := cmYes;
  665.  
  666.                      { If ok to delete then do so }
  667.                      if Affirmative= cmYes then
  668.                         begin
  669.  
  670.                         { Delete the focused item}
  671.                         PList(List)^.Delete(
  672.                            PList(List)^.AT(FocusedIndex));
  673.  
  674.                         { Now pack the list }
  675.                         PList(list)^.pack;
  676.  
  677.                         { Update LISTBOX  }
  678.                         if FocusedIndex>=PList(list)^.count then
  679.                             UpdateLB(FocusedIndex-1)
  680.                           else
  681.                             UpdateLB(focusedIndex);
  682.                         end;
  683.                    end;
  684.                end;
  685.            end;
  686.         end;
  687.   {==================================================================
  688.    TSortedListBoxDialog  Class
  689.    ==================================================================}
  690.      constructor TSortedListDialog.init
  691.                    (var Bounds:Trect; ATitle:TTitleStr;Behavior:byte;
  692.                     TheList : PSortedList; BoxHeader:TTitleStr);
  693.       var
  694.         R:Trect;
  695.         SB:PSCrollBar;
  696.         i:word;
  697.         TMax:word;
  698.         Fill:String[80];
  699.       {-------------------------------------}
  700.       procedure GetMAx(P:pointer); far;
  701.         var
  702.           I:word;
  703.           Temp:string;
  704.         begin
  705.         Temp := TheList^.GetItemText(P,$ff);
  706.         i:=length(Temp);
  707.         if i>TMax then TMax := i;
  708.         end;
  709.       {-------------------------------------}
  710.         begin
  711.        { Get Max Text Width of Tlist Items }
  712.         Tmax := 0;
  713.         Thelist^.foreach(@GetMax);
  714.  
  715.         BASICinit(Bounds,ATitle,Behavior,TMax);
  716.  
  717.        { Save List }
  718.         List := TheList;
  719.  
  720.        { Save Max String Legnth }
  721.         Max := TMax;
  722.  
  723.        { Ok now set up a scrollbar }
  724.         i:=(Bounds.B.Y-Bounds.A.Y)-4;
  725.         R.assign(Max+2,Y+2,Max+3,i);
  726.         SB := new(PScrollBar, init(R));
  727.         insert(SB);
  728.  
  729.        { Ok now setup ListBox }
  730.         R.assign(1,Y+2,Max+2,i);
  731.         LB := new(PSortedlistBoxer, init(R,1,SB));
  732.        { Setup Initial Data to List Box, will be chnaged by
  733.          SetData later}
  734.         LB^.newlist(TheList);
  735.         LB^.FocusItem(0);
  736.         insert(LB);
  737.         { Add Box Header }
  738.         if BoxHeader <> '' then
  739.           begin
  740.           R.assign(1,Y+1,length(BoxHeader)+2,Y+2);
  741.           insert(new(Plabel,init(R,BoxHeader,LB)));
  742.           end;
  743.  
  744.       { Clear Search Field }
  745.         FillChar(Fill[1],Max,32);
  746.         Fill[0] := Char(max);
  747.         SearchString := newstr(Fill);
  748.         SearchString^ := '';
  749.  
  750.       { Set behavior or search }
  751.         AB := AB or Behavior;
  752.       end;
  753.     {-------------------------------------------------}
  754.      procedure TSortedListDialog.HandleEvent(var Event:TEvent);
  755.      var
  756.        OldValue: Integer;
  757.        Affirmative : word;
  758.        FocusedIndex:integer;
  759.        FocusedItem:pointer;
  760.        NextEvent:TEvent;
  761.        MsgStr,ParamStr:Pstring;
  762.        R:Trect;
  763.       {--------------------------}
  764.        procedure KeySearch(KeyStr:PString);
  765.         var
  766.           i:integer;
  767.         begin
  768.         PSortedList(List)^.search(KeyStr,i);
  769.         LB^.focusItem(i);
  770.         {++}
  771.         If X>0 then
  772.           writestr(X,Y,Lput(SearchString^,Max),SearchPaletteMap);
  773.         ClearEvent(Event);
  774.        end;
  775.       {--------------------------}
  776.       procedure UpdateLB(Index:integer);
  777.         begin
  778.       { A real No No ! But it is the only way to update
  779.         LB and keep the List from being disposed! }
  780.         LB^.List := nil;
  781.         LB^.newlist(List);
  782.         LB^.focusItem(Index);
  783.         LB^.drawview;
  784.         end;
  785.       {--------------------------}
  786.  
  787.      begin
  788.       if (Event.What=evCommand) then
  789.         case Event.Command of
  790.          { OK It was selected we are ready to exit, Save data }
  791.          cmOk:
  792.            with TLR do
  793.              begin
  794.              Index := LB^.Focused;
  795.              Item:=  PSortedList(List)^.at(Index);
  796.              end;
  797.          { Whoops, a cancel, make sure nil is loaded }
  798.          cmCancel,CmQuit:
  799.            with TLR do
  800.              begin
  801.              Index := EndOfCollection;
  802.              Item:=  nil;
  803.              end;
  804.          end;
  805.        if (Event.What<>evkeydown)or(Event.keycode<>$3920) then
  806.          TDialog.HandleEvent(Event);
  807.        OldValue := LB^.Focused;
  808.        if LB^.GetState(sfFocused) and
  809.         { Do not let List Box Use the SpaceBar to select }
  810.         (not ((Event.What=evKeyDown)and(Event.KeyCode=$3920))) then
  811.              LB^.HandleEvent(Event);
  812.        if (OldValue <> LB^.Focused) then
  813.          begin
  814.          if X>0 then
  815.            begin
  816.            {++}
  817.            SearchString^ := '';
  818.            writestr(X,Y,Lput(SearchString^,Max),SearchPaletteMap);
  819.            end;
  820.          end
  821.        else
  822.        begin
  823.        FocusedIndex := LB^.Focused;
  824.        with Event do
  825.            case What of
  826.              evKeyDown:
  827.               if (Event.CharCode <> #0)  then
  828.                 begin
  829.                 case KeyCode of
  830.                    kbback:
  831.                      if Length(SearchString^)>0 then
  832.                        SearchString^[0] := char(length(SearchString^)-1);
  833.                   else
  834.                     if (length(SearchString^)<Max) and
  835.                     (CharCode > #31)and(ScanCode<>0) then
  836.                       SearchString^ := SearchString^+ charCode;
  837.  
  838.                   end;
  839.                 KeySearch(SearchString);
  840.                 end;
  841.              evCommand:
  842.                case Command of
  843.               { Ok it was picked }
  844.                  tldpicked:
  845.                    begin
  846.                    with NextEvent do
  847.                      { If prompt then move to OK Button }
  848.                      if (AB and sfPromptExit)>0 then
  849.                        begin
  850.                        Selectnext(true);
  851.                        Selectnext(true);
  852.                        end
  853.                      else
  854.                        { Else Set CmOK }
  855.                        begin
  856.                        What := evCommand;
  857.                        command := cmOk;
  858.                        end;
  859.                    putevent(NextEvent);
  860.                    end;
  861.  
  862.               { Add Record }
  863.                  tldAdd:
  864.                    with PSortedList(List)^ do
  865.                      begin
  866.  
  867.                    { OK Add a new Item, check if nil afterward }
  868.                      R.Assign(1,1,0,0);
  869.                      MakeGlobal(R.A,R.A);
  870.                      FocusedItem := AtAddNewItem(R.A);
  871.                      if FocusedItem <> nil then
  872.                        begin
  873.                        FocusedIndeX := indexOf(FocusedItem);
  874.                        UpdateLB(FocusedIndex);
  875.                        end;
  876.                      end;
  877.  
  878.                { Edit Record }
  879.                  tldEdit:
  880.                    begin
  881.                    R.Assign(1,1,0,0);
  882.                    MakeGlobal(R.A,R.A);
  883.                    FocusedItem := PSortedList(List)^.at(LB^.Focused);
  884.                    with PSortedList(List)^ do
  885.                      EditItem(R.A,FocusedItem);
  886.                    PSortedList(List)^.Delete(FocusedItem);
  887.                    PSortedList(List)^.insert(FocusedItem);
  888.                    PSortedList(list)^.pack;
  889.                    UpdateLB(PsortedList(list)^.indexof(focusedItem));
  890.                    end;
  891.  
  892.                { Delete Record }
  893.                  tldDelete:
  894.                    { Make sure something is there}
  895.                    if PsortedList(list)^.count>0 then
  896.                      begin
  897.  
  898.                      { If prompt then prompt }
  899.                      if (AB and sfPromptDelete)>0 then
  900.                        begin
  901.                        with PSortedList(List)^ do
  902.                          ParamStr := newstr(GetItemText(AT(FocusedIndex),Max));
  903.                          MsgStr := newStr('Delete: %s');
  904.                        Affirmative :=
  905.                          MessageBox(MsgSTr^,@ParamStr,
  906.                                     MFConfirmation+MfYesButton+MfNoButton);
  907.                        disposestr(Paramstr);
  908.                        disposestr(MsgStr);
  909.                        end
  910.                      else
  911.                        Affirmative := cmYes;
  912.  
  913.                      { If ok to delete then do so }
  914.                      if Affirmative= cmYes then
  915.                         begin
  916.  
  917.                         { Delete the focused item}
  918.                         PSortedList(List)^.Delete(
  919.                            PSortedList(List)^.AT(FocusedIndex));
  920.  
  921.                         { Now pack the list }
  922.                         PSortedList(list)^.pack;
  923.  
  924.                         { Update LISTBOX  }
  925.                         if FocusedIndex>=PsortedList(list)^.count then
  926.                             UpdateLB(FocusedIndex-1)
  927.                           else
  928.                             UpdateLB(focusedIndex);
  929.                         end;
  930.                    end;
  931.                end;
  932.            end;
  933.         end;
  934.      end;
  935.    {======================================================
  936.     TListDialogInputField
  937.     ======================================================}
  938.     constructor TListDialogInputField.init
  939.                      (Field:TPoint;ListLocation:Tpoint;ListHeight:word;
  940.                       Title:string;Behavior:byte;AList:Pointer;
  941.                       BoxHeader:string;SortedList:boolean);
  942.       var
  943.         R:Trect;
  944.         Tmax:byte;
  945.         Corner:TPoint;
  946.       begin
  947.         {Finds Max Size }
  948.         if SortedList then
  949.           TMax := PSortedlist(Alist)^.MaxTextLength
  950.         else
  951.           TMax := Plist(Alist)^.MaxTextLength;
  952.  
  953.         { Locate and initialize field }
  954.         R.assign(Field.X,Field.Y,Field.X+TMax+3,Field.Y+1);
  955.         TInputLine.init(R,TMax+2);
  956.  
  957.         {initialize Slots }
  958.         Sorted := SortedList;
  959.         TL := Alist;
  960.         Max := Tmax;
  961.  
  962.         { determine R based on bounds of owner of TInputLine }
  963.         MakeGlobal(Field,Field);
  964.         Field.X := Field.X + ListLocation.X;
  965.         Field.Y := Field.Y + ListLocation.Y;
  966.         MakeTrect(Field,Max+13,ListHeight-1,R);
  967.  
  968.        { Initialize ListDialog }
  969.         if Sorted then
  970.           begin
  971.           TD := new(PSortedListDialog,Init(R,Title,Behavior,AList,BoxHeader));
  972.           with PSortedList(Alist)^ do
  973.             Data^ := GetItemText(AT(0),max);
  974.           end
  975.         else
  976.           begin
  977.           TD := new(PListDialog,Init(R,Title,Behavior,AList,BoxHeader));
  978.           with PList(Alist)^ do
  979.             Data^ := GetItemText(AT(0),max);
  980.           end;
  981.       end;
  982.  
  983.   {-----------------------------------------------------}
  984.     procedure TListDialogInputField.HandleEvent(Var Event:TEvent);
  985.    {------------------------}
  986.       procedure OpenListDialog;
  987.        var
  988.          TCData : TlistRec;
  989.          Result:word;
  990.        begin
  991.          TCData.index := index;
  992.          if Sorted then
  993.            begin
  994.            TCData.item := PSortedList(TL)^.at(index);
  995.            PSortedListDialog(TD)^.setdata(TCData);
  996.            result := Desktop^.ExecView(PSortedListDialog(TD));
  997.            end
  998.          else
  999.            begin
  1000.            TCData.item := PList(TL)^.at(index);
  1001.            PListDialog(TD)^.setdata(TCData);
  1002.            result := Desktop^.ExecView(PListDialog(TD));
  1003.            end;
  1004.          If Result = cmOk then
  1005.            begin
  1006.            if Sorted then
  1007.              begin
  1008.              PSortedListDialog(TD)^.Getdata(TCData);
  1009.              Data^ :=PSortedList(TL)^.getitemtext(TCData.item,max);
  1010.              end
  1011.            else
  1012.              begin
  1013.              PListDialog(TD)^.Getdata(TCData);
  1014.              Data^ :=PList(TL)^.getitemtext(TCData.item,max);
  1015.              end;
  1016.            Index :=  TCData.index;
  1017.            end
  1018.          else
  1019.            CLearEvent(Event);
  1020.        end;
  1021.     {======================================}
  1022.       begin
  1023.         with Event do
  1024.           case What of
  1025.             evMousedown:
  1026.                begin
  1027.                  if double and getstate(sffocused+sfselected) then
  1028.                    OpenListDialog
  1029.                end;
  1030.             evKeyDown:
  1031.               case KeyCode of
  1032.                 kbins,kbRight,kbLeft,kbCtrlF2:
  1033.                   begin
  1034.                   OpenListDialog;
  1035.                   end;
  1036.                 kbenter,kbdown:
  1037.                   begin
  1038.                   KeyCode := kbTab;
  1039.                   end;
  1040.                 kbup:
  1041.                   begin
  1042.                   KeyCode := kbShiftTab;
  1043.                   end;
  1044.                end;
  1045.              end;
  1046.         TInputLine.HandleEvent(Event);
  1047.       end;
  1048.   {-----------------------------------------------------}
  1049.     function  TListDialogInputField.DataSize:word;
  1050.       begin DataSize := 2; end;
  1051.    {------------------------------------------------------}
  1052.     procedure TListDialogInputField.GetData(Var Rec);
  1053.       var Value:word absolute rec;
  1054.       begin Value := index; end;
  1055.    {------------------------------------------------------}
  1056.     procedure TListDialogInputField.SetData(Var Rec);
  1057.       var
  1058.         Value:word absolute Rec;
  1059.       begin
  1060.         if (Value = EndOfCollection)or(Value >= PCOllection(TL)^.Count) then
  1061.           Index := PCollection(TL)^.count -1
  1062.         else
  1063.           Index := Value;
  1064.       if sorted then
  1065.         data^ := PSortedList(TL)^.getItemText(PList(TL)^.at(index),max)
  1066.       else
  1067.         data^ := PList(TL)^.getItemText(PSortedList(TL)^.at(index),max);
  1068.       end;
  1069.   {-------------------------------------------------}
  1070.   procedure RegisterTVList;
  1071.     begin
  1072.     RegisterType(RList);
  1073.     RegisterType(RSortedList);
  1074.     end;
  1075.   {-------------------------------------------------}
  1076.  
  1077.   {$Ifdef RegisterStreams }
  1078.    begin
  1079.    RegisterTVList;
  1080.   {$EndIf}
  1081. end.
  1082.  
  1083.   { Notes:
  1084.     Need to allow control of placemnet of edit and delte Dialogs, perhaps
  1085.       with a set Location procedure.
  1086.     Need to fill in a search string when dialog is evoked, try putting
  1087.       a write in dialog.draw?
  1088.     Need to create a TDialog class that traps (space) different and
  1089.       allows cursor  keys to move among fields
  1090.   }
  1091.